
/*主要实现了文件工具类和json工具类,二者分别对fstream,ofstream和json等进行了封装,简化操作*/

#ifndef __UTIL_HPP__
#define __UTIL_HPP__

#include <fstream>
#include <string>
#include <vector>
#include <sys/stat.h>
#include <openssl/md5.h>
#include <jsoncpp/json/json.h>
#include <experimental/filesystem>
#include "bundle/bundle.h"
#include "logging-system/logs/logs/log.h"

namespace cloud
{
    class MD5Util
    {
    public:
        static std::string calculateMD5(const std::string &data)
        {
            unsigned char digest[MD5_DIGEST_LENGTH];
            MD5(reinterpret_cast<const unsigned char *>(data.c_str()), data.length(), digest);

            char hash[MD5_DIGEST_LENGTH * 2 + 1];
            for (int i = 0; i < MD5_DIGEST_LENGTH; ++i)
            {
                sprintf(&hash[i * 2], "%02x", (unsigned int)digest[i]);
            }

            return std::string(hash);
        }
    };
    namespace fs = std::experimental::filesystem;
    class FileUtil
    {
    private:
        std::string _filename; // 文件名
    public:
        FileUtil(const std::string &name) : _filename(name)
        {
        }

        // 获取文件大小
        size_t FileSize()
        {
            struct stat tmp;
            if (stat(_filename.c_str(), &tmp) < 0)
            {
                Log::getLogger("globalLogger")->warn("获取文件:%s 大小出错!!!", FileName().c_str());
                return -1;
            }
            return tmp.st_size;
        }

        std::string GetMD5()
        {
            std::string data;
            GetContent(&data);
            return MD5Util::calculateMD5(data);
        }

        // 上次访问时间
        time_t LastATime()
        {
            struct stat tmp;
            if (stat(_filename.c_str(), &tmp) < 0)
            {
                Log::getLogger("globalLogger")->warn("获取文件:%s 上次访问时间出错!!!", FileName().c_str());
                return -1;
            }
            return tmp.st_atime;
        }

        // 上次修改时间
        time_t LastMTime()
        {
            struct stat tmp;
            if (stat(_filename.c_str(), &tmp) < 0)
            {
                Log::getLogger("globalLogger")->warn("获取文件:%s 上次修改时间出错!!!", FileName().c_str());
                return -1;
            }
            return tmp.st_mtime;
        }

        // 返回文件名
        const std::string FileName()
        {
            size_t pos = _filename.find_last_of("/");
            if (pos == string::npos)
                return _filename;
            else
                return _filename.substr(pos + 1);
        }

        // 从指定位置返回指定长度内容
        bool GetPosLen(std::string *content, size_t pos, size_t len)
        {
            if (pos + len > this->FileSize())
            {
                Log::getLogger("globalLogger")->warn("获取文件:%s 长度大于文件实际长度!!!", FileName().c_str());
                return false;
            }

            std::ifstream ifs;
            ifs.open(_filename, std::ios::binary);

            if (ifs.is_open() == false)
            {
                Log::getLogger("globalLogger")->warn("打开文件:%s 失败!!!", FileName().c_str());
                return false;
            }
            ifs.seekg(pos, std::ios::beg); // 以开头为基准向后偏移pos长度
            content->resize(len);
            ifs.read(&(*content)[0], len);
            if (ifs.good() == false)
            {
                Log::getLogger("globalLogger")->warn("获取文件:%s 内容失败!!!", FileName().c_str());
                ifs.close();
                return false;
            }
            ifs.close();
            return true;
        }

        // 返回文件内容
        bool GetContent(std::string *content)
        {
            return GetPosLen(content, 0, FileSize());
        }

        // 设置文件内容
        bool SetContent(const std::string &content)
        {
            std::ofstream ofs;
            ofs.open(_filename, std::ios::binary);
            if (ofs.is_open() == false)
            {
                Log::getLogger("globalLogger")->warn("打开文件%s 失败!!!", FileName().c_str());
                ofs.close();
                return false;
            }
            ofs.write(content.c_str(), content.size());
            if (ofs.good() == false)
            {
                Log::getLogger("globalLogger")->warn("写入文件%s 失败!!!", FileName().c_str());
                ofs.close();
                return false;
            }
            return true;
        }

        // 生成压缩文件
        bool Compress(const std::string &packname, std::string *ret = nullptr)
        {
            std::string content;
            if (GetContent(&content) == false)
            {
                Log::getLogger("globalLogger")->warn("压缩文件:%s 失败!!!", FileName().c_str());
                return false;
            }

            FileUtil pack(packname);

            std::string packed = bundle::pack(bundle::LZIP, content);
            if (ret != nullptr)
                *ret = packed;
            if (pack.SetContent(packed) == false)
            {
                Log::getLogger("globalLogger")->warn("压缩文件: %s失败!!!", FileName().c_str());
                return false;
            }
            Log::getLogger("globalLogger")->_info("压缩文件: %s完成!!!", FileName().c_str());
            return true;
        }

        // 解压文件
        bool UnCompress(const std::string &filename)
        {
            std::string packed;
            if (GetContent(&packed) == false)
            {
                Log::getLogger("globalLogger")->warn("解压文件: %s失败!!!", FileName().c_str());
                return false;
            }

            std::string content = bundle::unpack(packed);

            FileUtil file(filename);
            if (file.SetContent(content) == false)
            {
                Log::getLogger("globalLogger")->warn("解压文件: %s失败!!!", FileName().c_str());
                return false;
            }

            return true;
        }

        // 判断文件是否存在
        bool Exists()
        {
            bool ret = fs::exists(_filename); // C++17
            if (ret == false)
            {
                Log::getLogger("globalLogger")->_info("打开文件: %s不存在!!!", FileName().c_str());
            }
            return ret;
        }

        // 删除文件
        bool Remove()
        {
            if (this->Exists() == false)
            {
                return true;
            }
            remove(_filename.c_str());
            return true;
        }

        // 创建目录
        bool CreateDirectory()
        {
            if (this->Exists() == true)
                return true;

            return fs::create_directories(_filename); // C++17
        }

        // 扫描目录并返回目录内容
        void ScanDirectory(std::vector<std::string> *arry)
        {
            __ScanDirectory(_filename, arry); // 可以递归遍历版本
            // for (auto &p : fs::directory_iterator(_filename))
            // {
            //     if (fs::is_directory(p) == true)
            //         continue; // 目录则无须理会

            //     // relative_path 带有路径的文件名
            //     arry->push_back(fs::path(p).relative_path().string());
            // }
        }

        void __ScanDirectory(const std::string &directory, std::vector<std::string> *arry)
        {
            for (const auto &entry : fs::directory_iterator(directory))
            {
                if (fs::is_directory(entry))
                {
                    // 如果是目录，则递归扫描子目录
                    __ScanDirectory(entry.path().string(), arry);
                }
                else
                {
                    // 如果是文件，则将其加入文件列表
                    arry->push_back(entry.path().string());
                }
            }
        }
    };

    class JsonUtil
    {
    public:
        static bool Serialize(Json::Value &root, std::string *str)
        {
            Json::StreamWriterBuilder builder;
            std::unique_ptr<Json::StreamWriter> writer(builder.newStreamWriter());

            std::stringstream ss;
            if (writer->write(root, &ss) != 0)
            {
                Log::getLogger("globalLogger")->warn("序列化失败!!!");
                return false;
            }
            *str = ss.str();
            return true;
        }

        static bool UnSerialize(Json::Value &root, const std::string *str)
        {
            Json::CharReaderBuilder builder;
            std::unique_ptr<Json::CharReader> reader(builder.newCharReader());

            std::string err;
            if (reader->parse(str->c_str(), str->c_str() + str->size(), &root, &err) == false)
            {
                Log::getLogger("globalLogger")->warn("反序列化内容: %s失败!!!", str->c_str());
                return false;
            }
            return true;
        }
    };

    class BundleUtil
    {
    public:
        static void UnCompress(const std::string &compress, std::string &unCompress)
        {
            unCompress = bundle::unpack(compress);
        }
        static void Compress(std::string &compress, const std::string &unCompress)
        {
            compress = bundle::unpack(unCompress);
        }
    };

}; // namespace cloude

#endif //__UTIL_HPP__
